package watcher

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"os/exec"
	//	pathpkg "path"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/bitly/go-simplejson"
	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/host"
	"github.com/shirou/gopsutil/load"
	"github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/net"
)

type MetrucExportor struct {
	wg  sync.WaitGroup
	wgp sync.WaitGroup
	//	ch           chan int
	mu           sync.RWMutex
	interval     int64
	lastScanTime int64
	value        map[string]map[string]float64 //metricname  yy="yy",xx="xx"
	metrics      []string
	ping_ips     string
	check_conn   *simplejson.Json
	//
	stopping       chan interface{}
	stopped        chan interface{}
	netLastValues  map[string]uint64
	cpuLastValues  map[string]float64
	diskLastValues map[string]uint64
}

func NewMetrucExportor(interval int64, ping_ips string, check_conn *simplejson.Json, metrics []string) *MetrucExportor {
	return &MetrucExportor{
		interval:   interval,
		metrics:    metrics,
		ping_ips:   ping_ips,
		check_conn: check_conn,
	}
}

func (me *MetrucExportor) GetValue() map[string]map[string]float64 {
	return me.value
}

func (me *MetrucExportor) Export(tags map[string]string) (string, error) {
	//	if err := me.refresh(); err != nil {
	//		return "", err
	//	}
	_, ok := tags["host"]
	if !ok {
		cmd := exec.Command("/bin/hostname", "-f")
		var out bytes.Buffer
		cmd.Stdout = &out
		err := cmd.Run()
		if err != nil {
			log.Error(`run "hostname -f":`, err)
			tags["host"], _ = os.Hostname()
		} else {
			fqdn := out.String()
			fqdn = fqdn[:len(fqdn)-1]
			tags["host"] = fqdn
		}
	}
	var buf bytes.Buffer
	for k, v := range tags {
		buf.WriteString(fmt.Sprintf(`%s="%s",`, k, v))
	}
	tagstr := buf.String()
	buf.Reset()
	me.mu.RLock()
	defer me.mu.RUnlock()
	for metricName, m := range me.value {
		if metricName == "uptime" {
			buf.WriteString(fmt.Sprintf(`# TYPE %s counter`, metricName))
			buf.WriteString("\n")
		} else {
			buf.WriteString(fmt.Sprintf(`# TYPE %s gauge`, metricName))
			buf.WriteString("\n")
		}
		for detail, v := range m {
			if tagstr == "" {
				buf.WriteString(fmt.Sprintf(`%s{%s} %.4f %d`, metricName, detail, v, me.lastScanTime))
				buf.WriteString("\n")
			} else {
				buf.WriteString(fmt.Sprintf(`%s{%s%s} %.4f %d`, metricName, tagstr, detail, v, me.lastScanTime))
				buf.WriteString("\n")
			}
		}
	}
	buf.WriteString("\n")
	return buf.String(), nil
}

const FT int64 = 1000000

func (me *MetrucExportor) getMetric() error {
	me.mu.Lock()
	for _, m := range me.metrics {
		var err error
		switch m {
		case "UpTimeAndProcs":
			err = me.getUpTimeAndProcs()
			//log.Info(1)
		case "Load":
			err = me.getLoad()
			//log.Info(2)
		case "Misc":
			err = me.getMisc()
			//log.Info(3)
		case "VirtualMemory":
			err = me.getVirtualMemory()
			//log.Info(4)
		case "SwapMemory":
			err = me.getSwapMemory()
			//log.Info(5)
		case "CPU":
			err = me.getCPU()
			//log.Info(6)
		case "NetIOCounters":
			err = me.getNetIOCounters()
			//log.Info(7)
		case "TcpStatus":
			err = me.getTcpStatus()
			//log.Info(8)
		case "DiskUsage":
			err = me.getDiskUsage()
			//log.Info(9)
		case "DiskIOCounters":
			err = me.getDiskIOCounters()
			//log.Info(10)
		case "NetPing":
			err = me.getNetPing()
			//log.Info(11)
		case "NetCheck":
			err = me.getNetCheck()
			//log.Info(12)
		default:
			err = fmt.Errorf("have no this metric [%s]", m)
		}
		if err != nil {
			return err
		}
	}
	me.lastScanTime = time.Now().UnixNano() / FT
	me.mu.Unlock()
	return nil
}

func (me *MetrucExportor) Refresh() {
	me.value = make(map[string]map[string]float64)
	err := me.getNetInfo()
	checkErr(err)
	err = me.getMemInfo()
	checkErr(err)
	err = me.getDiskInfo()
	checkErr(err)
	err = me.getCpuInfo()
	checkErr(err)
	tick := time.Tick(time.Millisecond * time.Duration(me.interval))
	for {
		select {
		case <-tick:
			err = me.getMetric()
			checkErr(err)
		case <-me.stopping:
			me.mu.Lock()
			me.mu.Unlock()
			close(me.stopped)
		}
	}
}

func (me *MetrucExportor) Close() {
	close(me.stopping)
	<-me.stopped
}

func pingIP(me *MetrucExportor, ip string) {
	cmdstr := `ping ` + ip + ` -c 1 -W 1|grep rtt|awk '{print $4}'`
	var out bytes.Buffer
	cmd := exec.Command(`/bin/sh`, `-c`, cmdstr)
	cmd.Stdout = &out
	err := cmd.Run()
	checkErr(err)
	sent := out.String()
	if sent == "" {
		me.value["net_ping"][`dest_host="`+ip+`",m="down",u="ms"`] = float64(-1)
		me.wg.Done()
		return
	}
	pv := strings.Split(strings.TrimSpace(sent), `/`)
	pt, _ := strconv.ParseFloat(pv[0], 64)
	me.value["net_ping"][`des_host="`+ip+`",m="ping_time",u="ms"`] = pt
	/*	min, _ := strconv.ParseFloat(pv[0], 64)
		avg, _ := strconv.ParseFloat(pv[1], 64)
		max, _ := strconv.ParseFloat(pv[2], 64)
		mdev, _ := strconv.ParseFloat(pv[3], 64)

		me.value["net_ping"][`des_host="`+ip+`",m="min",u="ms"`] = min
		me.value["net_ping"][`des_host="`+ip+`",m="avg",u="ms"`] = avg
		me.value["net_ping"][`des_host="`+ip+`",m="max",u="ms"`] = max
		me.value["net_ping"][`des_host="`+ip+`",m="mdev",u="ms"`] = mdev*/
	me.wg.Done()
	//me.ch <- 1
	return
}

func checkNet(me *MetrucExportor, ip, port, clu, proj string) {
	cmdstr := `echo -e "\n"|telnet ` + ip + ` ` + port + `|grep Connected`
	cmd := exec.Command(`/bin/sh`, `-c`, cmdstr)
	out, _ := cmd.CombinedOutput()
	cmdout := string(out)
	if strings.Contains(cmdout, `refused`) {
		me.value["net_check"][`dest_host="`+ip+`",port="`+port+`",subcluster="`+clu+`",subproj="`+proj+`",m="down"`] = float64(-1)
	}
	if strings.Contains(cmdout, `Connected`) {
		me.value["net_check"][`dest_host="`+ip+`",port="`+port+`",subcluster="`+clu+`",subproj="`+proj+`",m="down"`] = float64(1)
		cmdstr2 := `curl -o /dev/null -m 3 -s -w %{time_total} ` + ip + `:` + port
		cmd2 := exec.Command(`/bin/sh`, `-c`, cmdstr2)
		out2, _ := cmd2.CombinedOutput()
		cmdout2 := string(out2)
		ct, _ := strconv.ParseFloat(strings.TrimSpace(cmdout2), 64)
		me.value["net_check"][`dest_host="`+ip+`",port="`+port+`",subcluster="`+clu+`",subproj="`+proj+`",m="check_time",u="ms"`] = ct * 1000
	}
	me.wgp.Done()
}

func (me *MetrucExportor) getNetCheck() error {
	if me.check_conn == nil {
		return nil
	}
	if _, ok := me.value["net_check"]; !ok {
		me.value["net_check"] = make(map[string]float64)
	}
	for i, _ := range me.check_conn.MustArray() {
		cf := me.check_conn.GetIndex(i)
		cp := cf.Get("cluster_proj").MustString()
		c_p := strings.Split(cp, `#`)
		clu := c_p[0]
		proj := c_p[1]
		ipt := cf.Get("ip_port").MustStringArray()
		for _, v := range ipt {
			ss := strings.Split(v, `:`)
			ip := ss[0]
			port := ss[1]
			me.wgp.Add(1)
			go checkNet(me, ip, port, clu, proj)
		}
	}
	me.wgp.Wait()
	return nil
}

func (me *MetrucExportor) getNetPing() error {
	if me.ping_ips == "" {
		return nil
	}
	if _, ok := me.value["net_ping"]; !ok {
		me.value["net_ping"] = make(map[string]float64)
	}
	ips := strings.Split(me.ping_ips, `/`)
	for _, ip := range ips {
		me.wg.Add(1)
		go pingIP(me, ip)
	}
	me.wg.Wait()
	/*	for i := 0; i < len(ips); i++ {
		<-me.ch
	}*/
	return nil
}

func (me *MetrucExportor) getCpuInfo() error {
	info, err := cpu.Info()
	if err != nil {
		return err
	}
	var cores int32 = 0
	for _, v := range info {
		cores = cores + v.Cores
	}
	if _, ok := me.value["cpu_info"]; !ok {
		me.value["cpu_info"] = make(map[string]float64)
	}
	me.value["cpu_info"][`m="cores",u="cores"`] = float64(cores)
	return nil
}

func (me *MetrucExportor) getDiskInfo() error {
	d, err := disk.Partitions(false)
	if err != nil {
		return err
	}
	if _, ok := me.value["disk_info"]; !ok {
		me.value["disk_info"] = make(map[string]float64)
	}
	for _, v := range d {
		u, err := disk.Usage(v.Mountpoint)
		if err != nil {
			continue
		}
		me.value["disk_info"][fmt.Sprintf(`path="%s",m="%s",u="GB"`, v.Device, "space")] = float64(u.Total) / 1024 / 1024 / 1024
	}
	return nil
}

func (me *MetrucExportor) getMemInfo() error {
	v, err := mem.VirtualMemory()
	if err != nil {
		return err
	}
	if _, ok := me.value["mem_info"]; !ok {
		me.value["mem_info"] = make(map[string]float64)
	}
	if v.Total != 0 {
		me.value["mem_info"][`m="total",u="GB"`] = float64(v.Total) / 1024 / 1024 / 1024
	}
	return nil
}

//func (me *MetrucExportor) getNetInfo() error {
//	if _, ok := me.value["net_info"]; !ok {
//		me.value["net_info"] = make(map[string]float64)
//	}
//	basepath := `/sys/class/net`
//	fs, err := ioutil.ReadDir(basepath)
//	if err != nil {
//		return err
//	}
//	for _, f := range fs {
//		path := pathpkg.Join(basepath, f.Name(), `speed`)
//		b, err := ioutil.ReadFile(path)
//		if err != nil {
//			continue
//		}
//		s := strings.TrimSpace(string(b))
//		speed, err := strconv.ParseFloat(s, 64)
//		if err != nil {
//			continue
//		}
//		me.value["net_info"][fmt.Sprintf(`path="%s",m="speed",u="Mb/s"`, f.Name())] = speed
//	}
//	return nil
//}

func (me *MetrucExportor) getNetInfo() error {
	info, err := net.Interfaces()
	if err != nil {
		return err
	}
	if _, ok := me.value["net_info"]; !ok {
		me.value["net_info"] = make(map[string]float64)
	}
	for _, v := range info {
		cmd1 := exec.Command(`ethtool`, v.Name)
		cmd2 := exec.Command(`grep`, `Speed`)
		var out bytes.Buffer
		cmd2.Stdin, _ = cmd1.StdoutPipe()
		cmd2.Stdout = &out
		cmd2.Start()
		cmd1.Run()
		cmd2.Wait()
		stat := out.String()
		if !strings.Contains(stat, `Speed:`) {
			continue
		}
		speed, err := parse(stat)
		if err != nil {
			continue
		}
		me.value["net_info"][fmt.Sprintf(`path="%s",m="speed",u="Mb/s"`, v.Name)] = speed
	}
	return nil
}

func parse(stat string) (speed float64, err error) {
	str := strings.Split(stat, `:`)
	s := strings.TrimSpace(str[1])
	ss := strings.Trim(s, `Mb/s`)
	speed, err = strconv.ParseFloat(ss, 64)
	return
}

func (me *MetrucExportor) getUpTimeAndProcs() error {
	info, err := host.Info()
	if err != nil {
		return err
	}
	if _, ok := me.value["host"]; !ok {
		me.value["host"] = make(map[string]float64)
	}
	me.value["host"][`m="uptime"`] = float64(info.Uptime)
	me.value["host"][`m="procs"`] = float64(info.Procs)
	return nil
}

func (me *MetrucExportor) getLoad() error {
	la, err := load.Avg()
	if err != nil {
		return err
	}
	if _, ok := me.value["load"]; !ok {
		me.value["load"] = make(map[string]float64)
	}
	me.value["load"][`m="load1"`] = float64(la.Load1)
	me.value["load"][`m="load5"`] = float64(la.Load5)
	me.value["load"][`m="load15"`] = float64(la.Load15)
	return nil
}

func (me *MetrucExportor) getMisc() error {
	lm, err := load.Misc()
	if err != nil {
		return err
	}
	if _, ok := me.value["misc"]; !ok {
		me.value["misc"] = make(map[string]float64)
	}
	me.value["misc"][`m="procsRunning"`] = float64(lm.ProcsRunning)
	me.value["misc"][`m="procsBlocked"`] = float64(lm.ProcsBlocked)
	//me.value["misc"][`m="ctxt"`] = float64(lm.Ctxt)
	return nil
}

func (me *MetrucExportor) getVirtualMemory() error {
	v, err := mem.VirtualMemory()
	if err != nil {
		return err
	}
	if _, ok := me.value["mem"]; !ok {
		me.value["mem"] = make(map[string]float64)
	}
	if v.Total != 0 {
		me.value["mem"][`m="used",u="persent"`] = float64(v.Used) / float64(v.Total)
		me.value["mem"][`m="available",u="persent"`] = float64(v.Available) / float64(v.Total)
		me.value["mem"][`m="free",u="persent"`] = float64(v.Free) / float64(v.Total)
		me.value["mem"][`m="active",u="persent"`] = float64(v.Active) / float64(v.Total)
		me.value["mem"][`m="inactive",u="persent"`] = float64(v.Inactive) / float64(v.Total)
		me.value["mem"][`m="buffers",u="persent"`] = float64(v.Buffers) / float64(v.Total)
		me.value["mem"][`m="cached",u="persent"`] = float64(v.Cached) / float64(v.Total)
		me.value["mem"][`m="shared",u="persent"`] = float64(v.Shared) / float64(v.Total)
		me.value["mem_info"][`m="used",u="GB"`] = float64(v.Used) / 1024 / 1024 / 1024
	}
	return nil
}

func (me *MetrucExportor) getSwapMemory() error {
	v, err := mem.SwapMemory()
	if err != nil {
		return err
	}
	if _, ok := me.value["mem"]; !ok {
		me.value["mem"] = make(map[string]float64)
	}
	if v.Total != 0 {
		me.value["mem"][`m="swap_used",u="persent"`] = float64(v.Used) / float64(v.Total)
		me.value["mem"][`m="swap_free",u="persent"`] = float64(v.Free) / float64(v.Total)
		me.value["mem"][`m="swap_sin",u="persent"`] = float64(v.Sin) / float64(v.Total)
		me.value["mem"][`m="swap_sout",u="persent"`] = float64(v.Sout) / float64(v.Total)
	}
	return nil
}

func CounterMinus(last, now map[string]uint64) map[string]float64 {
	ret := make(map[string]float64)
	if last == nil {
		return ret
	}
	for k, v := range now {
		lastV, ok := last[k]
		if ok {
			ret[k] = float64(v - lastV)
		}
	}
	return ret
}

func (me *MetrucExportor) getNetIOCounters() error {
	interval := float64(me.interval) / 1000
	ncs, err := net.IOCounters(true)
	if err != nil {
		return err
	}
	netNowValues := make(map[string]uint64)
	for _, nc := range ncs {
		name := nc.Name
		netNowValues[name+"_bytes_sent"] = nc.BytesSent
		netNowValues[name+"_bytes_recv"] = nc.BytesRecv
		netNowValues[name+"_packets_sent"] = nc.PacketsSent
		netNowValues[name+"_packets_recv"] = nc.PacketsRecv
		netNowValues["all_bytes_sent"] += nc.BytesSent
		netNowValues["all_bytes_recv"] += nc.BytesRecv
		netNowValues["all_packets_sent"] += nc.PacketsSent
		netNowValues["all_packets_recv"] += nc.PacketsRecv
	}
	if _, ok := me.value["net_iorate"]; !ok {
		me.value["net_iorate"] = make(map[string]float64)
	}

	netValues := CounterMinus(me.netLastValues, netNowValues)
	me.netLastValues = netNowValues
	netret := make(map[string]float64)
	for _, nc := range ncs {
		netret[fmt.Sprintf(`path="%s",m="%s"`, nc.Name, "bytes_sent/s")] = netValues[nc.Name+"_bytes_sent"] / interval
		netret[fmt.Sprintf(`path="%s",m="%s"`, nc.Name, "bytes_recv/s")] = netValues[nc.Name+"_bytes_recv"] / interval
		netret[fmt.Sprintf(`path="%s",m="%s"`, nc.Name, "packets_sent/s")] = netValues[nc.Name+"_packets_sent"] / interval
		netret[fmt.Sprintf(`path="%s",m="%s"`, nc.Name, "packets_recv/s")] = netValues[nc.Name+"_packets_recv"] / interval
	}
	netret[fmt.Sprintf(`path="all",m="%s"`, "bytes_sent/s")] = netValues["all_bytes_sent"] / interval
	netret[fmt.Sprintf(`path="all",m="%s"`, "bytes_recv/s")] = netValues["all_bytes_recv"] / interval
	netret[fmt.Sprintf(`path="all",m="%s"`, "packets_sent/s")] = netValues["all_packets_sent"] / interval
	netret[fmt.Sprintf(`path="all",m="%s"`, "packets_recv/s")] = netValues["all_packets_recv"] / interval
	me.value["net_iorate"] = netret
	fs, err := ioutil.ReadDir("/sys/class/net")
	if err != nil {
		return err
	}
	for _, v := range fs {
		if _, ok := me.value["net_info"][fmt.Sprintf(`path="%s",m="speed",u="Mb/s"`, v.Name())]; !ok {
			continue
		}
		me.value["net_info"][fmt.Sprintf(`path="%s",m="sentPersent"`, v.Name())] = me.value["net_iorate"][fmt.Sprintf(`path="%s",m="%s"`, v.Name(), "bytes_sent/s")] / 131072 / me.value["net_info"][fmt.Sprintf(`path="%s",m="speed",u="Mb/s"`, v.Name())]
		me.value["net_info"][fmt.Sprintf(`path="%s",m="recvPersent"`, v.Name())] = me.value["net_iorate"][fmt.Sprintf(`path="%s",m="%s"`, v.Name(), "bytes_recv/s")] / 131072 / me.value["net_info"][fmt.Sprintf(`path="%s",m="speed",u="Mb/s"`, v.Name())]
	}
	return nil
}

func (me *MetrucExportor) getDiskIOCounters() error {
	interval := float64(me.interval) / 1000
	diskNowValues := make(map[string]uint64)
	dcs, err := disk.IOCounters()
	if err != nil {
		return err
	}
	for name, dc := range dcs {
		diskNowValues[name+"rcount"] = dc.ReadCount
		diskNowValues[name+"wcount"] = dc.WriteCount
		diskNowValues[name+"mrcount"] = dc.MergedReadCount
		diskNowValues[name+"mwcount"] = dc.MergedWriteCount
		diskNowValues[name+"rbytes"] = dc.ReadBytes
		diskNowValues[name+"wbytes"] = dc.WriteBytes
		diskNowValues[name+"rtime"] = dc.ReadTime
		diskNowValues[name+"wtime"] = dc.WriteTime
		diskNowValues[name+"iotime"] = dc.IoTime
		diskNowValues[name+"weightedIO"] = dc.WeightedIO
	}
	if _, ok := me.value["disk_iorate"]; !ok {
		me.value["disk_iorate"] = make(map[string]float64)
	}
	diskValues := CounterMinus(me.diskLastValues, diskNowValues)
	me.diskLastValues = diskNowValues
	diskret := make(map[string]float64)
	for name, _ := range dcs {
		diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "rrqm/s")] = diskValues[name+"mrcount"] / interval
		diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "wrqm/s")] = diskValues[name+"mwcount"] / interval
		diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "r/s")] = diskValues[name+"rcount"] / interval
		diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "w/s")] = diskValues[name+"wcount"] / interval
		diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "rkB/s")] = diskValues[name+"rbytes"] / 1024 / interval
		diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "wkB/s")] = diskValues[name+"wbytes"] / 1024 / interval
		diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "rd_sec/s")] = diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "rkB/s")] * 2
		diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "wr_sec/s")] = diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "wkB/s")] * 2
		diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "tps")] = (diskValues[name+"rcount"] + diskValues[name+"wcount"]) / interval
		diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "avgqu-sz")] = diskValues[name+"weightedIO"] / float64(me.interval)
		diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "util")] = diskValues[name+"iotime"] / float64(me.interval)
		if diskValues[name+"rcount"] == 0 && diskValues[name+"wcount"] == 0 {
			diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "r_await")] = 0
			diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "w_await")] = 0
			diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "avgrq-sz")] = 0
			diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "await")] = 0
			diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "svctm")] = 0
		} else {
			diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "avgrq-sz")] = (diskValues[name+"rbytes"] + diskValues[name+"wbytes"]) / (diskValues[name+"rcount"] + diskValues[name+"wcount"]) / 512
			diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "await")] = (diskValues[name+"rtime"] + diskValues[name+"wtime"]) / (diskValues[name+"rcount"] + diskValues[name+"wcount"])
			diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "svctm")] = diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "util")] / (diskValues[name+"rcount"] + diskValues[name+"wcount"])
			if diskValues[name+"rcount"] != 0 {
				diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "r_await")] = diskValues[name+"rtime"] / diskValues[name+"rcount"]
			} else {
				diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "r_await")] = 0
			}
			if diskValues[name+"wcount"] != 0 {
				diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "w_await")] = diskValues[name+"wtime"] / diskValues[name+"wcount"]
			} else {
				diskret[fmt.Sprintf(`path="%s",m="%s"`, name, "w_await")] = 0
			}
		}
	}
	me.value["disk_iorate"] = diskret
	return nil
}

func (me *MetrucExportor) getTcpStatus() error {
	b, err := ioutil.ReadFile("/proc/net/tcp")
	if err != nil {
		return err
	}
	if _, ok := me.value["tcp_conn"]; !ok {
		me.value["tcp_conn"] = make(map[string]float64)
	}
	for k, v := range ParseTcpCount(b) {
		me.value["tcp_conn"][fmt.Sprintf(`status="%s"`, k)] = float64(v)
	}
	return nil
}

func (me *MetrucExportor) getDiskUsage() error {
	d, err := disk.Partitions(false)
	if err != nil {
		return err
	}
	if _, ok := me.value["disk_usage"]; !ok {
		me.value["disk_usage"] = make(map[string]float64)
	}
	for _, v := range d {
		u, err := disk.Usage(v.Mountpoint)
		if err != nil {
			continue
		}
		me.value["disk_info"][fmt.Sprintf(`path="%s",m="%s",u="GB"`, v.Device, "used")] = float64(u.Used) / 1024 / 1024 / 1024
		me.value["disk_usage"][fmt.Sprintf(`path="%s",m="%s",u="persent"`, v.Mountpoint, "space")] = u.UsedPercent / 100
		me.value["disk_usage"][fmt.Sprintf(`path="%s",m="%s",u="persent"`, v.Mountpoint, "inode")] = u.InodesUsedPercent / 100
	}
	cmdstr := `du -s /var|awk '{print $1}'`
	cmd := exec.Command(`/bin/sh`, `-c`, cmdstr)
	out, _ := cmd.CombinedOutput()
	cmdout := string(out)
	sp, _ := strconv.ParseFloat(strings.TrimSpace(cmdout), 64)
	me.value["disk_usage"][fmt.Sprintf(`path="%s",m="%s",u="GB"`, `/var`, "used")] = sp / 1024 / 1024
	return nil
}

const TOTAL = "total"

func PercentMinus(last, now map[string]float64) map[string]float64 {
	lt, ok1 := last[TOTAL]
	nt, ok2 := now[TOTAL]
	ret := make(map[string]float64)
	if ok1 && ok2 {
		total := float64(nt - lt)
		for k, v := range now {
			if k == TOTAL {
				continue
			}
			if lastv, ok := last[k]; ok {
				ret[k] = float64(v-lastv) / total
			}
		}
	}
	ret["used"] = 1 - ret["idle"]
	return ret
}

func (me *MetrucExportor) getCPU() error {
	v, err := cpu.Times(false)
	if err != nil || len(v) != 1 {
		return err
	}
	cpuNowValues := make(map[string]float64)
	cpuNowValues["user"] = v[0].User
	cpuNowValues["system"] = v[0].System
	cpuNowValues["idle"] = v[0].Idle
	cpuNowValues["iowait"] = v[0].Iowait
	cpuNowValues["irq"] = v[0].Irq
	cpuNowValues["nice"] = v[0].Nice
	cpuNowValues["softirq"] = v[0].Softirq
	cpuNowValues["steal"] = v[0].Steal
	cpuNowValues["guest"] = v[0].Guest
	cpuNowValues["guestNice"] = v[0].GuestNice
	cpuNowValues["stolen"] = v[0].Stolen
	cpuNowValues["total"] = v[0].User + v[0].System + v[0].Nice + v[0].Iowait + v[0].Irq + v[0].Softirq + v[0].Steal + v[0].Guest + v[0].GuestNice + v[0].Stolen + v[0].Idle
	if _, ok := me.value["cpu"]; !ok {
		me.value["cpu"] = make(map[string]float64)
	}
	for k, v := range PercentMinus(me.cpuLastValues, cpuNowValues) {
		me.value["cpu"][fmt.Sprintf(`m="%s",u="persent"`, k)] = v
	}
	cores := me.value["cpu_info"][`m="cores",u="cores"`] * me.value["cpu"][`m="used"`]
	c := math.Floor(cores)
	if cores-c > 0.1 {
		cores = c + 1
	} else {
		cores = c
	}
	me.value["cpu_info"][`m="used_cores",u="cores"`] = cores
	me.cpuLastValues = cpuNowValues
	return nil
}
